﻿
namespace App17
{
    using System;
    using System.Linq;
    using Windows.UI.Xaml;
    using System.Threading.Tasks;
    using Windows.UI.Xaml.Controls;
    using System.Collections.Generic;

    public sealed partial class MainPage : Page
    {
        ViewModels.IMainPageViewModel ViewModel { get { return this.DataContext as ViewModels.IMainPageViewModel; } }

        public MainPage()
        {
            this.InitializeComponent();
            Loaded += MainPage_Loaded;
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            this.ViewModel.AfterFormat += (s, args) => MyGridView.Update();
            this.MyGridView.SelectionChanged += (s, args) => MyGridView.ScrollIntoView(MyGridView.SelectedItem);
        }
    }

    namespace ViewModels
    {
        using System.Collections.ObjectModel;
        using System.Reflection;

        partial class Locator : Common.BindableBase
        {
            IMainPageViewModel sMainPageViewModel = default(IMainPageViewModel);
            public IMainPageViewModel MainPageViewModel
            {
                get
                {
                    if (sMainPageViewModel != default(IMainPageViewModel))
                        return sMainPageViewModel;
                    if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
                        sMainPageViewModel = new DesigntimeMainPageViewModel(Repository.Locator.Current);
                    else
                        sMainPageViewModel = new RuntimeMainPageViewModel(Repository.Locator.Current);
                    sMainPageViewModel.Start();
                    return sMainPageViewModel;
                }
            }
        }

        interface IMainPageViewModel
        {
            ObservableCollection<Models.ColorInfo> Colors { get; }
            Models.ColorInfo Selected { get; set; }
            event EventHandler AfterFormat;
            void Start();
        }

        class DesigntimeMainPageViewModel : Common.ViewModelBase, IMainPageViewModel
        {
            public Repository.IColorRepository _ColorRepository;
            public DesigntimeMainPageViewModel(Repository.IColorRepository colorRepository)
            { _ColorRepository = colorRepository; }

            public virtual void Start()
            {
                foreach (var color in _ColorRepository.Colors())
                    this.Colors.Add(color);
                this.Selected = this.Colors.First();
            }

            ObservableCollection<Models.ColorInfo> _Colors = new ObservableCollection<Models.ColorInfo>();
            public ObservableCollection<Models.ColorInfo> Colors { get { return _Colors; } }

            Models.ColorInfo _Selected = default(Models.ColorInfo);
            public Models.ColorInfo Selected { get { return _Selected; } set { base.SetProperty(ref _Selected, value); Format(); } }

            public event EventHandler AfterFormat;

            protected virtual void Format()
            {
                foreach (var color in Colors)
                {
                    color.RowSpan = 1;
                    color.ColSpan = 1;
                }
                if (Selected != null)
                {
                    Selected.RowSpan = 2;
                    Selected.ColSpan = 5;
                }
                if (AfterFormat != null)
                    AfterFormat(this, EventArgs.Empty);
            }
        }

        class RuntimeMainPageViewModel : DesigntimeMainPageViewModel
        {
            public RuntimeMainPageViewModel(Repository.IColorRepository colorRepository)
                : base(colorRepository) { }

            public override void Start()
            {
                // TODO
                base.Start();
            }

            protected override void Format()
            {
                // TODO
                base.Format();
            }
        }
    }

    namespace Models
    {
        class ColorInfo : Common.ModelBase
        {
            public string Name { get; set; }
            public Windows.UI.Color Color { get; set; }
            public Windows.UI.Xaml.Media.SolidColorBrush Brush { get { return new Windows.UI.Xaml.Media.SolidColorBrush(this.Color); } }
        }
    }

    namespace Repository
    {
        using System.Reflection;

        interface IColorRepository
        {
            Models.ColorInfo[] Colors();
        }

        static class Locator
        {
            static IColorRepository sCurrent = default(IColorRepository);
            public static IColorRepository Current
            {
                get
                {
                    if (sCurrent != default(IColorRepository))
                        return sCurrent;
                    if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
                        return sCurrent = new DesignColorRepository();
                    else
                        return sCurrent = new RuntimeColorRepository();
                }
            }
        }

        class DesignColorRepository : IColorRepository
        {
            public Models.ColorInfo[] Colors()
            {
                var colors = typeof(Windows.UI.Colors).GetRuntimeProperties()
                    .OrderBy(x => Guid.NewGuid())
                    .Select(x => new Models.ColorInfo { Name = x.Name, Color = (Windows.UI.Color)x.GetValue(null) });
                return colors.ToArray();
            }
        }

        class RuntimeColorRepository : DesignColorRepository { /* TODO */ }
    }

    namespace Common
    {
        abstract class ModelBase : Common.BindableBase
        {
            int _ColSpan = default(int);
            public int ColSpan { get { return _ColSpan; } set { base.SetProperty(ref _ColSpan, value); } }
            int _RowSpan = default(int);
            public int RowSpan { get { return _RowSpan; } set { base.SetProperty(ref _RowSpan, value); } }
            public override string ToString()
            {
                return string.Format("{0}:{1}-{2}", ColSpan, RowSpan, base.ToString());
            }
        }

        abstract class ViewModelBase : Common.BindableBase { }

        abstract class BindableBase : System.ComponentModel.INotifyPropertyChanged
        {
            public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
            protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
            {
                if (!object.Equals(storage, value))
                {
                    storage = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
                }
            }
            protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }

    namespace Controls
    {
        class VariableGridView : GridView
        {
            public void Update()
            {
                if (!(this.ItemsPanelRoot is VariableSizedWrapGrid))
                    throw new ArgumentException("ItemsPanel is not VariableSizedWrapGrid");
                foreach (var container in this.ItemsPanelRoot.Children.Cast<GridViewItem>())
                {
                    var data = container.Content as Common.ModelBase;
                    VariableSizedWrapGrid.SetRowSpan(container, data.RowSpan);
                    VariableSizedWrapGrid.SetColumnSpan(container, data.ColSpan);
                }
                this.ItemsPanelRoot.InvalidateMeasure();
            }

            protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
            {
                var model = item as Common.ModelBase;
                try
                {
                    element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, model.ColSpan);
                    element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.RowSpanProperty, model.RowSpan);
                }
                catch
                {
                    element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, 1);
                    element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.RowSpanProperty, 1);
                }
                finally
                {
                    base.PrepareContainerForItemOverride(element, item);
                }
            }
        }
    }
}